package com.ysstech.common.aspect;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ysstech.common.annotation.YssLog;
import com.ysstech.common.cache.RedisCacheManager;
import com.ysstech.common.entity.Logger;
import com.ysstech.common.entity.User;
import com.ysstech.common.util.DateUtil;
import com.ysstech.common.service.LoggerService;
import com.ysstech.common.util.SpringContextUtil;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 日志切面  所有service层的exception直接跑出，这里日志会记录
 */
@Slf4j
@Aspect
@Component
public class SystemLogAspect {
    /**
     * 进入方法时间戳
     */
    private Long startTime;
    /**
     * 方法结束时间戳(计时)
     */
    private Long endTime;

    /**
     * 定义请求日志切入点，其切入点表达式有多种匹配方式,这里是指定路径
     */
    @Pointcut("@annotation(com.ysstech.common.annotation.YssLog)")
    public void webLogPointcut() {
    }

    /**
     * 前置通知：
     * 1. 在执行目标方法之前执行，比如请求接口之前的登录验证;
     * 2. 在前置通知中设置请求日志信息，如开始时间，请求参数，注解内容等
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLogPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        try {
            // 接收到请求，记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //获取请求头中的User-Agent
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
            //打印请求的内容
            startTime = System.currentTimeMillis();
            log.info("请求开始时间：{}", LocalDateTime.now());
            log.info("方法描述:{}", getServiceMthodDescription(joinPoint));
            log.info("请求Url : {}", request.getRequestURL().toString());
            log.info("请求方式 : {}", request.getMethod());
            log.info("请求ip : {}", request.getRemoteAddr());
            log.info("请求方法 : ", joinPoint.getTarget().getClass().getName());
            log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
            // 系统信息
            log.info("浏览器：{}", userAgent.getBrowser().toString());
            log.info("浏览器版本：{}", userAgent.getBrowserVersion());
            log.info("操作系统: {}", userAgent.getOperatingSystem().toString());
            Logger logger = new Logger();
            logger.setIp(request.getRemoteAddr());
            logger.setBrowser(userAgent.getBrowser().toString());
            logger.setUrl(request.getRequestURL().toString());
            logger.setRequestType(request.getMethod());
            //请求的参数
         /*   Object[] args = joinPoint.getArgs();
            if(null != args && args.length >0){
                try{
                    logger.setRequestInfo(JsonUtil.objectToJson(args[0]));
                }catch (Exception e){
                }
            }*/
            logger.setRequestInfo(joinPoint.getArgs().toString());
            logger.setMethodDescription(getServiceMthodDescription(joinPoint));
            logger.setClassPath(joinPoint.getTarget().getClass().getName());
            logger.setBrowser(userAgent.getBrowser().toString());
            logger.setBrowserVersion(userAgent.getBrowserVersion().toString());
            logger.setOperatingSystem(userAgent.getOperatingSystem().toString());
            logger.setStatus(1);
            insertLogger(logger);
        } catch (Exception ex) {
            log.error("异常信息:{}", ex.getMessage());
        }
    }

    private void insertLogger(Logger logger) throws Exception {
        RedisCacheManager redisCacheManager = SpringContextUtil.getBean(RedisCacheManager.class);
        logger.setId(DateUtil.generateUUID());
        logger.setAddTime(LocalDateTime.now());
        try {
            User user = redisCacheManager.getLoginUserInfo();
            logger.setUserId(user.getName());
            logger.setOrgId(user.getOrganization().getFullName());
        } catch (Exception ex) {
            log.error("异常信息:{}", ex.getMessage());
        }
        LoggerService loggerService = SpringContextUtil.getBean(LoggerService.class);
        loggerService.save(logger);
        QueryWrapper<Logger> wrapper = new QueryWrapper<>();
        wrapper.le("ADD_TIME", LocalDateTime.now().minusDays(1));
        loggerService.remove(wrapper);
    }

    /**
     * 返回通知：
     * 1. 在目标方法正常结束之后执行
     * 1. 在返回通知中补充请求日志信息，如返回时间，方法耗时，返回值，并且保存日志信息
     *
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "webLogPointcut()")
    public void doAfterReturning(Object ret) throws Throwable {
        endTime = System.currentTimeMillis();
        log.info("请求结束时间：{}", LocalDateTime.now());
        log.info("请求耗时：{}", (endTime - startTime));
        // 处理完请求，返回内容
        log.info("请求返回 : {}", ret);
    }


    /**
     * 获取注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public static String getServiceMthodDescription(JoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = "【" + method.getAnnotation(YssLog.class).module() + "】"
                            + "【" + method.getAnnotation(YssLog.class).remark() + "】";
                    break;
                }
            }
        }
        return description;
    }


    /**
     * 异常通知
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "webLogPointcut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        try {
            // 接收到请求，记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //获取请求头中的User-Agent
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
            log.info("方法描述:{}", getServiceMthodDescription(joinPoint));
            log.info("请求Url : {}", request.getRequestURL().toString());
            log.info("请求方式 : {}", request.getMethod());
            log.info("请求ip : {}", request.getRemoteAddr());
            log.info("请求方法 : ", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
            // 系统信息
            log.info("浏览器：{}", userAgent.getBrowser().toString());
            log.info("浏览器版本：{}", userAgent.getBrowserVersion());
            log.info("操作系统: {}", userAgent.getOperatingSystem().toString());
            log.info("错误信息: {}", e.getMessage());
            Logger logger = new Logger();
            logger.setIp(request.getRemoteAddr());
            logger.setBrowser(userAgent.getBrowser().toString());
            logger.setUrl(request.getRequestURL().toString());
            logger.setRequestType(request.getMethod());
            logger.setRequestInfo(joinPoint.getArgs().toString());
            logger.setMethodDescription(getServiceMthodDescription(joinPoint));
            logger.setClassPath(joinPoint.getTarget().getClass().getName());
            logger.setBrowser(userAgent.getBrowser().toString());
            logger.setBrowserVersion(userAgent.getBrowserVersion().toString());
            logger.setOperatingSystem(userAgent.getOperatingSystem().toString());
            logger.setErrorInfo(e.getMessage());
            logger.setStatus(2);
            insertLogger(logger);
        } catch (Exception ex) {
            log.error("异常信息:{}", ex.getMessage());
        }
    }

}
